import {GameEvent} from "db://assets/script/game-event";
import {director} from "cc";
import {FishFryDetail, FreshImmortal, ImmortalDetail} from "db://assets/proto/shared/v1/wuni";
import {http} from "db://assets/script/common/HTTP";
import {
    FishBagReceiveRequest,
    FishBagReceiveResult,
    FishCreateRequest,
    FishCreateResult,
    FishFryComeRequest,
    FishFryComeResult,
    FishFryListRequest,
    FishFryListResult, FishFryRefuseRequest, FishFryRefuseResult,
    FishFryTakeRequest,
    FishFryTakeResult,
    FishListenRequest,
    FishListenResult, FishPoolRankRequest, FishPoolRankResult,
    FishRefiningRequest,
    FishRefiningResult,
    FishSeeRequest,
    FishSeeResult
} from "db://assets/proto/new-world-api/v1/fish";
import {lobby} from "db://assets/script/lobby";
import {FishCoreChangeEvent} from "db://assets/script/common/events/fish-events/fish-core-change-event";
import {UserFishRefineEvent} from "db://assets/script/common/events/fish-events/user-fish-refine-event";
import {Fish} from "db://assets/script/game/entity/fish";
import {UserFishListenEvent} from "db://assets/script/common/events/fish-events/user-fish-listen-event";
import {UserFishAddOthersEvent} from "db://assets/script/common/events/fish-events/user-fish-add-others-event";
import {UserFishRemoveOthersEvent} from "db://assets/script/common/events/fish-events/user-fish-remove-others-event";
import {UserFishCreateEvent} from "db://assets/script/common/events/fish-events/user-fish-create-event";
import {UserFishReceiveBagEvent} from "db://assets/script/common/events/fish-events/user-fish-receive-bag-event";
import {AssetCoreChange} from "db://assets/script/common/events/asset-events/asset-core-change";
import Long from "long";
import {UserFishFryListEvent} from "db://assets/script/common/events/fish-events/user-fish-fry-list-event";
import {UserFishFryListLoadedEvent} from "db://assets/script/common/events/fish-events/user-fish-fry-list-loaded-event";
import {UserFishFryTakeEvent} from "db://assets/script/common/events/fish-events/user-fish-fry-take-event";
import {
    UserFishFryTakeCompleteEvent
} from "db://assets/script/common/events/fish-events/user-fish-fry-take-complete-event";
import {UserFishFryComeEvent} from "db://assets/script/common/events/fish-events/user-fish-fry-come-event";
import {
    UserFishFryListCompleteEvent
} from "db://assets/script/common/events/fish-events/user-fish-fry-list-complete-event";
import {AssetId} from "db://assets/proto/shared/v1/props";
import {UserFishRanksRequestEvent} from "db://assets/script/common/events/fish-events/user-fish-ranks-request-event";
import {UserFishRanksResultEvent} from "db://assets/script/common/events/fish-events/user-fish-ranks-result-event";
import {cmm} from "db://assets/script/utils/cmm";
import {UserFishFryRefuseEvent} from "db://assets/script/common/events/fish-events/user-fish-fry-refuse-event";
import {
    UserFishFryRefuseCompleteEvent
} from "db://assets/script/common/events/fish-events/user-fish-fry-refuse-complete-event";
import {
    FishAttackRecordReply,
    FishBattleCompleteReply, FishDeadReply,
    FishEarnReply,
    FishGrowthReply,
    FishMagicPowerRecoverReply, FishPreAttackReply,
    FishRecoverReply
} from "db://assets/proto/new-world-ws/v1/internal";
import {fishUtils} from "db://assets/script/utils/fish-utils";
import {FishStatus} from "db://assets/proto/shared/v1/biz";


export class UserFishInfo {
    private fishFries: Map<string, FishFryDetail> = new Map()
    private fishFriesCome: Map<string, FishFryDetail> = new Map()

    private _fishes: Map<string, Fish> = new Map();
    private _fishParking: Map<string, Fish> = new Map();
    private _otherFishes: Map<string, FreshImmortal> = new Map();
    private _fishFryInDormitory: Map<string, FishFryDetail[]> = new Map();

    currentShowFishInBattle: Long | null = null

    // private _actionCallback: Map<string, (body: Uint8Array) => void> = new Map();

    start() {
        this.onActionEvent()
        this.onRequestEvent()
        this.onNotifyEvent()
        this.onDataChangeEvent()
    }

    // action为操作类的请求，请求仅发起，返回值仅有基础的资产校验相关，具体操作结果通过ws返回
    // action不会变更本地数据，数据变更需要等到ws返回后进行
    onActionEvent() {
        director.on(GameEvent.userFishActionRefine, this._actionUserFishRefine, this)
        director.on(GameEvent.userFishActionListen, this._actionUserFishListen, this)
        director.on(GameEvent.userFishActionCreate, this._actionUserFishCreate, this)
        director.on(GameEvent.userFishAddOthers, this._addOtherFish, this)
        director.on(GameEvent.userFishRemoveOthers, this._removeOtherFish, this)
    }

    onRequestEvent() {
        director.on(GameEvent.userFishRequestReceiveFishBag, this._requestFishBagReceive, this)
        director.on(GameEvent.userFishRequestFishFryCome, this._requestFishFryCome, this)
        director.on(GameEvent.userFishFryRequestTake, this._requestFishFryTake, this)
        director.on(GameEvent.userFishFryRequestRefuse, this._requestFishFryRefuse, this)
        director.on(GameEvent.userFishFryRequestList, this._requestFishFryList, this)
        director.on(GameEvent.userFishRequestRanks, this._requestFishRanks, this)
    }

    onNotifyEvent() {
        director.on(GameEvent.fishBeginBattle, this._onNotifyAttackBegin, this)
        director.on(GameEvent.fishInBattle, this._onNotifyAttackRecord, this)
        director.on(GameEvent.fishBeginRecover, this._onNotifyRecover, this)
        director.on(GameEvent.fishBeginEnergyRecover, this._onNotifyEnergyRecover, this)
        director.on(GameEvent.fishBeginDead, this._onNotifyDead, this)
        director.on(GameEvent.fishBeginEarn, this._onNotifyEarn, this)
        director.on(GameEvent.fishBeginGrowth, this._onNotifyGrowth, this)
        director.on(GameEvent.fishBattleComplete, this._onNotifyBattleComplete, this)
    }

    offNotifyEvent() {
        director.off(GameEvent.fishBeginBattle, this._onNotifyAttackBegin, this)
        director.off(GameEvent.fishInBattle, this._onNotifyAttackRecord, this)
        director.off(GameEvent.fishBeginRecover, this._onNotifyRecover, this)
        director.off(GameEvent.fishBeginEnergyRecover, this._onNotifyEnergyRecover, this)
        director.off(GameEvent.fishBeginDead, this._onNotifyDead, this)
        director.off(GameEvent.fishBeginEarn, this._onNotifyEarn, this)
        director.off(GameEvent.fishBeginGrowth, this._onNotifyGrowth, this)
        director.off(GameEvent.fishBattleComplete, this._onNotifyBattleComplete, this)
    }

    offRequestEvent() {
        director.off(GameEvent.userFishRequestReceiveFishBag, this._requestFishBagReceive, this)
        director.off(GameEvent.userFishRequestFishFryCome, this._requestFishFryCome, this)
        director.off(GameEvent.userFishFryRequestTake, this._requestFishFryTake, this)
        director.off(GameEvent.userFishFryRequestList, this._requestFishFryList, this)
    }


    offActionEvent() {
        director.off(GameEvent.userFishActionRefine, this._actionUserFishRefine, this)
        director.off(GameEvent.userFishActionListen, this._actionUserFishListen, this)
        director.off(GameEvent.userFishActionCreate, this._actionUserFishCreate, this)
        director.off(GameEvent.userFishAddOthers, this._addOtherFish, this)
        director.off(GameEvent.userFishRemoveOthers, this._removeOtherFish, this)
    }

    onDataChangeEvent() {
        director.on(GameEvent.fishCoreChange, this.fishCoreChange, this)
    }

    offDataChangeEvent() {
        director.off(GameEvent.fishCoreChange, this.fishCoreChange, this)
    }

    /**获取鱼列表 */
    loadFishes() {
        return this._fishes
    }

    /**获取鱼列表 */
    loadFirstFish() {
        return this._fishes.values().next().value
    }

    /**获取宿舍中的鱼苗列表 */
    loadDormitoryFishFries(buid: Long) {
        if (this._fishFryInDormitory.has(buid.toString())) {
            let ffs = this._fishFryInDormitory.get(buid.toString())!
            ffs = ffs.filter((item) => this.fishFries.has(item.fishId.toString()))
            this._fishFryInDormitory.set(buid.toString(), ffs)
            return ffs
        }
        return []
    }


    /**获取鱼根据id */
    loadFishById(id: Long) {
        if (this._fishes.has(id.toString())) {
            return this._fishes.get(id.toString())!.freshFish
        } else {
            return this._otherFishes.get(id.toString())
        }
    }

    loadCurrentFish() {
        if (this.currentShowFishInBattle)
            return this._fishes.get(this.currentShowFishInBattle.toString())?.freshFish
        else return undefined
    }

    /**设置鱼的列表 */
    setFishes(fishes: ImmortalDetail[]) {
        if (fishes) {
            for (let item of fishes) {
                const f = new Fish(item)
                this._fishes.set(item.fish!.id.toString(), f)
                this._fishParking.set(item.parking, f)
            }
        }
    }

    /**添加鱼 */
    addFish(fish: ImmortalDetail) {
        const f = new Fish(fish)
        this._fishes.set(fish.fish!.id.toString(), f);
        this._fishParking.set(fish.parking, f);
        director.emit(GameEvent.userFishAdd, fish)
    }

    /**添加非自己的鱼 */
    private _addOtherFish(event: UserFishAddOthersEvent) {
        this._otherFishes.set(event.otherFish.id.toString(), event.otherFish)
    }

    /**移除非自己的鱼 */
    private _removeOtherFish(event: UserFishRemoveOthersEvent) {
        const ff = this._otherFishes.get(event.fishId?.toString())
        if (ff) {
            director.emit(GameEvent.userFishRemove, ff)
            this._otherFishes.delete(event.fishId.toString())
        }
    }


    async seeOneFish(fishId: Long, zoneId: Long): Promise<FreshImmortal | void> {
        const param = FishSeeRequest.create({
            fishId: fishId,
            zoneId: zoneId,
        })
        return http.requestAsync("POST", "v1/fish/see", FishSeeResult, FishSeeRequest.encode(param).finish())
            .then(res => {
                    console.log('see fish', res)
                    this._otherFishes.set(res.fish!.id.toString(), res.fish!)
                    return res.fish!
                }
            )
            .catch(() => lobby.instance.showToast("查看失败"))
    }

    /**炼化鱼 */
    removeFishById(id: Long) {
        if (this._fishes.has(id.toString())) {
            const fish = this._fishes.get(id.toString())!
            fish.offActionEvent()
            fish.offNotifyEvent()
            director.emit(GameEvent.userFishRemove, fish.freshFish)
            this._fishes.delete(id.toString())
        }
    }

    /**移除鱼苗 */
    removeFishFryById(id: Long) {
        this.fishFries.delete(id.toString())
    }

    fishCoreChange(event: FishCoreChangeEvent) {
        const ff = this.loadFishById(event.fishId)
        if (ff) {
            event.changeFunc(ff)
            director.emit(GameEvent.uiUpdateFish, event.fishId)
        }
    }


    async _actionUserFishRefine(event: UserFishRefineEvent) {
        const param = FishRefiningRequest.create({fishId: event.fishId})
        return http.requestAsync("POST", "v1/fish/refining", FishRefiningResult, FishRefiningRequest.encode(param).finish())
            .then(_ => this.removeFishById(event.fishId))
            .catch((reason) => {
                console.log(reason)
                lobby.instance.showToast("安葬失败")
            })
    }

    async _actionUserFishListen(event: UserFishListenEvent) {
        const param = FishListenRequest.create({
            fishId: event.fishId,
            zoneId: event.zoneId,
        })
        return http.requestAsync("POST", "v1/fish/listen", FishListenResult, FishListenRequest.encode(param).finish())
            .catch((reason) => {
                console.log(reason)
            })
    }

    async _actionUserFishCreate(event: UserFishCreateEvent) {
        const param = FishCreateRequest.create({
            fishId: event.fishId,
            aliveDuration: Long.fromNumber(3600000),
            daoWenId: event.daoWenId,
        })
        return http.requestAsync<FishCreateResult>("POST", "v1/fish/create", FishCreateResult, FishCreateRequest.encode(param).finish())
            .then(res => {
                this.addFish(ImmortalDetail.create({
                    fish: res.fish!,
                    parking: res.parking!,
                    bagPropCount: 0
                }))
                // res.fish?.zoneId
                this.removeFishFryById(event.fishId)
                director.emit(GameEvent.uiChangeAliveDuration, res.fish!.goHomeAtMs)
                director.emit(GameEvent.uiChangeZoneName, res.fish!.zoneId)
            })
            .catch((err: any) => {
                    console.error(err)
                    lobby.instance.showToast("接引弟子失败")
                }
            )
    }

    async _requestFishFryCome(_: UserFishFryComeEvent) {
        const param = FishFryComeRequest.create({})
        return http.requestAsync<FishFryComeResult>("POST", "v1/fish/fry/come", FishFryComeResult, FishFryComeRequest.encode(param).finish())
            .then(res => {
                let hasNew = false
                // let newFishId
                for (let nff of res.list) {
                    if (!this.fishFriesCome.has(nff.fishId.toString())) {
                        hasNew = true
                        this.fishFriesCome.set(nff.fishId.toString(), nff)
                    }
                }
                if (hasNew) director.emit(GameEvent.userFishFryNewCome, 1)
                director.emit(GameEvent.userFishFryComeLoaded, new UserFishFryListLoadedEvent(res.list))
            })
            .catch((_: any) => lobby.instance.showToast("获取失败"))
    }


    async _requestFishFryTake(event: UserFishFryTakeEvent) {
        const param = FishFryTakeRequest.create({
            fishId: event.fishId,
        })
        return http.requestAsync<FishFryTakeResult>("POST", "v1/fish/fry/take", FishFryTakeResult, FishFryTakeRequest.encode(param).finish())
            .then(res => {
                director.emit(GameEvent.userFishFryTakeComplete, new UserFishFryTakeCompleteEvent(res.fishFry!))
                if (this._fishFryInDormitory.has(res.fishFry!.liveIn.toString())) {
                    this._fishFryInDormitory.get(res.fishFry!.liveIn.toString())!.push(res.fishFry!)
                }
                const cost: { [key: number]: Long } = {}
                cost[Number(AssetId.asset_id_gold)] = Long.fromNumber(-500)
                director.emit(GameEvent.assetCoreChange, new AssetCoreChange(cost))
                lobby.instance.showToast('招收成功')
            })
            .catch((_: any) => lobby.instance.showToast("获取失败"))
    }

    async _requestFishFryRefuse(event: UserFishFryRefuseEvent) {
        const param = FishFryRefuseRequest.create({
            fishId: event.fishId,
        })
        return http.requestAsync<FishFryRefuseResult>("POST", "v1/fish/fry/refuse", FishFryRefuseResult, FishFryRefuseRequest.encode(param).finish())
            .then(res => {
                director.emit(GameEvent.userFishFryRefuseComplete, new UserFishFryRefuseCompleteEvent(event.fishId!))
                lobby.instance.showToast('仙路残酷，劝返成功')
            })
            .catch((_: any) => lobby.instance.showToast("劝返失败"))
    }

    async _requestFishFryList(event: UserFishFryListEvent) {
        const buid = event.buid.toString()
        const param = FishFryListRequest.create({
            buid: event.buid,
            page: 1,
            pageSize: 50,
        })
        return http.requestAsync<FishFryListResult>("POST", "v1/fish/fry/list", FishFryListResult, FishFryListRequest.encode(param).finish())
            .then(res => {
                this._fishFryInDormitory.set(buid, res.list)
                director.emit(GameEvent.userFishFryListResult, new UserFishFryListCompleteEvent(event.buid, res.list!))
            })
            .catch((_: any) => lobby.instance.showToast("请求失败"))
    }


    async _requestFishBagReceive(event: UserFishReceiveBagEvent) {
        const param = FishBagReceiveRequest.create({
            fishId: event.fishId.toString(),
            zoneId: event.zoneId,
        })
        return http.requestAsync<FishBagReceiveResult>("POST", "v1/fish/bag/receive", FishBagReceiveResult, FishBagReceiveRequest.encode(param).finish())
            .then(res => {
                console.log('received ', res.props)
                director.emit(GameEvent.assetCoreChange, new AssetCoreChange(cmm.map2kv(res.props, (k1, v1) => {
                    return {
                        k2: k1.toNumber(),
                        v2: v1
                    }
                })))
                const ff = this.loadFishById(event.fishId)
                if (ff) {
                    ff.assetInfo!.props = cmm.longMapMerge(ff.assetInfo!.props, res.props, (v1, v2) => {
                        return v1.sub(v2)
                    })
                    event.propBag?.changeAssetsNum(cmm.map2kv(ff.assetInfo!.props, (k1, v1) => {
                        return {
                            k2: k1.toNumber(),
                            v2: v1
                        }
                    }))
                } else {
                    event.propBag?.clearBag()
                }
                event.receiveCallback()
                lobby.instance.showToast("领取成功")
            })
            .catch((_: any) => lobby.instance.showToast("领取失败"))
    }

    async _requestFishRanks(event: UserFishRanksRequestEvent) {
        const param = FishPoolRankRequest.create({
            page: event.page,
            pageSize: event.pageSize,
            rankType: event.rankType,
            zoneId: event.zoneId,
        })
        return http.requestAsync("POST", "v1/fish/pool/rank", FishPoolRankResult, FishPoolRankRequest.encode(param).finish())
            .then(res => director.emit(GameEvent.userFishRanksResult, new UserFishRanksResultEvent(res)))
            .catch(() => console.log("获取排行榜失败"))
    }

    private _onNotifyGrowth(event: FishGrowthReply) {
        director.emit(GameEvent.fishCoreChange, new FishCoreChangeEvent(event.fishId, (ff) => {
            const cost = fishUtils.calcGrowthRequireMoney(ff.assetInfo!.level)
            ff.assetInfo!.level = event.growthLevel + 1
            ff.assetInfo!.money = ff.assetInfo!.money.sub(cost)
            event.records.forEach(record => {
                fishUtils.processGrowthValue(ff, record)
            })
        }))
    }

    private _onNotifyEnergyRecover(event: FishMagicPowerRecoverReply) {
        director.emit(GameEvent.fishCoreChange, new FishCoreChangeEvent(event.fishId, (ff) => {
            ff.battleInfo!.currentMagicPower = event.currentMagicPower
            ff.battleInfo!.finalBattleInfo!.magicPower = event.magicPower
            ff.battleInfo!.finalBattleInfo!.magicPowerBonus = event.magicPowerBonus
        }))
    }

    private _onNotifyRecover(event: FishRecoverReply) {
        director.emit(GameEvent.fishCoreChange, new FishCoreChangeEvent(event.fishId, (ff) => {
            ff.battleInfo!.currentNormalHealth = event.currentNormalHealth!
            ff.battleInfo!.finalBattleInfo!.normalHealth = event.normalHealth
            ff.battleInfo!.finalBattleInfo!.normalHealthBonus = event.normalHealthBonus
        }));
    }

    private _onNotifyEarn(event: FishEarnReply) {
        director.emit(GameEvent.fishCoreChange, new FishCoreChangeEvent(event.fishId, (ff) => {
            ff.assetInfo!.money = event.currentMoney
        }))
    }

    private _onNotifyBattleComplete(event: FishBattleCompleteReply) {
        director.emit(GameEvent.fishCoreChange, new FishCoreChangeEvent(event.fishId, (ff) => {
            ff.status = FishStatus.alive
            ff.completeAtMs = Long.fromNumber(-1)
        }))
    }

    private _onNotifyAttackRecord(event: FishAttackRecordReply) {
        director.emit(GameEvent.fishCoreChange, new FishCoreChangeEvent(event.defenceId, (ff) => {
            ff.battleInfo!.currentNormalHealth = event.defenceNormalHeal!
            ff.battleInfo!.finalBattleInfo!.normalHealth = event.defenceMaxNormalHeal
            ff.battleInfo!.finalBattleInfo!.normalHealthBonus = event.defenceMaxNormalHealBonus
            ff.battleInfo!.currentMagicPower = event.defenceMagicPower
            ff.battleInfo!.finalBattleInfo!.magicPower = event.defenceMaxMagicPower
            ff.battleInfo!.finalBattleInfo!.magicPowerBonus = event.defenceMaxMagicPowerBonus
            ff.status = FishStatus.in_battle
        }))
        director.emit(GameEvent.fishCoreChange, new FishCoreChangeEvent(event.attackId, (ff) => {
            ff.battleInfo!.currentNormalHealth = event.attackNormalHeal!
            ff.battleInfo!.finalBattleInfo!.normalHealth = event.attackMaxNormalHealth
            ff.battleInfo!.finalBattleInfo!.normalHealthBonus = event.attackMaxNormalHealthBonus
            ff.battleInfo!.currentMagicPower = event.attackMagicPower
            ff.battleInfo!.finalBattleInfo!.magicPower = event.attackMaxMagicPower
            ff.battleInfo!.finalBattleInfo!.magicPowerBonus = event.attackMaxMagicPowerBonus
            ff.status = FishStatus.in_battle
        }))
    }

    private _onNotifyDead(event: FishDeadReply) {
        director.emit(GameEvent.fishCoreChange, new FishCoreChangeEvent(event.fishId, (ff) => {
            ff.status = FishStatus.dead
            ff.battleInfo = event.fish!.battleInfo
            ff.baseInfo = event.fish!.baseInfo
            ff.assetInfo = event.fish!.assetInfo
            ff.completeAtMs = event.fish!.completeAtMs
        }))
    }

    /**战斗状态, 分发攻击与受攻击事件 */
    private _onNotifyAttackBegin(body: FishPreAttackReply) {
        console.log('begin battle: attacker ', body.attacker.toString(), 'defender', body.defender.toString())
        director.emit(GameEvent.fishCoreChange, new FishCoreChangeEvent(body.attacker, (ff) => {
            ff.status = FishStatus.pre_battle
        }))
        director.emit(GameEvent.fishCoreChange, new FishCoreChangeEvent(body.defender, (ff) => {
            ff.status = FishStatus.pre_battle
        }))
    }

    protected onDestroy(): void {
        this.offActionEvent()
        this.offRequestEvent()
        this.offDataChangeEvent()
        this.offNotifyEvent()
    }


}

